/**
 * 将 GeoJSON 对象从 GCJ-02 坐标系（高德、腾讯地图）转换为 WGS-84 坐标系（天地图、GPS）。
 *
 * @param {object} geoJson - 输入的 GeoJSON 对象（GCJ-02 坐标系）。
 * @returns {object} - 转换后的新 GeoJSON 对象（WGS-84 坐标系）。
 */
function convertGcj02ToWgs84(geoJson) {
  // 为了不修改原始 GeoJSON 对象，我们使用 JSON 方法进行深拷贝。
  // 这是一种简单有效的方式，可以确保输入数据不被污染。
  const wgs84GeoJson = JSON.parse(JSON.stringify(geoJson));

  // 递归遍历 GeoJSON 的坐标并进行转换
  traverseCoordinates(wgs84GeoJson, function (coordinates) {
    const gcj02Lon = coordinates[0];
    const gcj02Lat = coordinates[1];
    const wgs84Coord = transform(gcj02Lon, gcj02Lat);
    // 直接修改原坐标数组
    coordinates[0] = wgs84Coord.lon;
    coordinates[1] = wgs84Coord.lat;
  });

  return wgs84GeoJson;
}

/**
 * 递归遍历 GeoJSON 对象中的所有坐标点，并对每个坐标点执行回调函数。
 *
 * @param {object} geoJson - GeoJSON 对象。
 * @param {function} callback - 对每个坐标点执行的回调函数，参数为坐标数组 [lon, lat]。
 */
function traverseCoordinates(geoJson, callback) {
  if (!geoJson || !geoJson.type) return;

  // 处理 Feature 对象
  if (geoJson.type === 'Feature') {
    traverseCoordinates(geoJson.geometry, callback);
    return;
  }

  // 处理 FeatureCollection 对象
  if (geoJson.type === 'FeatureCollection') {
    for (let i = 0; i < geoJson.features.length; i++) {
      traverseCoordinates(geoJson.features[i], callback);
    }
    return;
  }

  // 处理 Geometry 对象
  const geometry = geoJson;
  if (geometry.coordinates) {
    switch (geometry.type) {
      case 'Point':
        callback(geometry.coordinates);
        break;
      case 'LineString':
      case 'MultiPoint':
        for (let i = 0; i < geometry.coordinates.length; i++) {
          callback(geometry.coordinates[i]);
        }
        break;
      case 'Polygon':
      case 'MultiLineString':
        for (let i = 0; i < geometry.coordinates.length; i++) {
          for (let j = 0; j < geometry.coordinates[i].length; j++) {
            callback(geometry.coordinates[i][j]);
          }
        }
        break;
      case 'MultiPolygon':
        for (let i = 0; i < geometry.coordinates.length; i++) {
          for (let j = 0; j < geometry.coordinates[i].length; j++) {
            for (let k = 0; k < geometry.coordinates[i][j].length; k++) {
              callback(geometry.coordinates[i][j][k]);
            }
          }
        }
        break;
      case 'GeometryCollection':
        for (let i = 0; i < geometry.geometries.length; i++) {
          traverseCoordinates(geometry.geometries[i], callback);
        }
        break;
    }
  }
}

/**
 * GCJ-02 坐标转换核心算法
 * 从 GCJ-02 (火星坐标系) 转换到 WGS-84 (世界大地坐标系)
 *
 * @param {number} gcjLon - GCJ-02 经度
 * @param {number} gcjLat - GCJ-02 纬度
 * @returns {{lon: number, lat: number}} - WGS-84 坐标对象
 */
function transform(gcjLon, gcjLat) {
  if (outOfChina(gcjLat, gcjLon)) {
    return { lon: gcjLon, lat: gcjLat };
  }
  let dLat = transformLat(gcjLon - 105.0, gcjLat - 35.0);
  let dLon = transformLon(gcjLon - 105.0, gcjLat - 35.0);
  const radLat = (gcjLat / 180.0) * Math.PI;
  let magic = Math.sin(radLat);
  magic = 1 - ee * magic * magic;
  const sqrtMagic = Math.sqrt(magic);
  dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.PI);
  dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI);

  const wgsLon = gcjLon - dLon;
  const wgsLat = gcjLat - dLat;

  return { lon: wgsLon, lat: wgsLat };
}

// --- 以下是转换算法所需的辅助函数和常量 ---

const a = 6378245.0;
const ee = 0.00669342162296594323;

function outOfChina(lat, lon) {
  return !(lon >= 72.004 && lon <= 137.8347 && lat >= 0.8293 && lat <= 55.8271);
}

function transformLat(x, y) {
  let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
  ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
  ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
  ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
  return ret;
}

function transformLon(x, y) {
  let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
  ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
  ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
  ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
  return ret;
}
